Skip to content

feat(import): add support for importing from Jump#1181

Open
vegerot wants to merge 1 commit intoajeetdsouza:mainfrom
vegerot:pr1181
Open

feat(import): add support for importing from Jump#1181
vegerot wants to merge 1 commit intoajeetdsouza:mainfrom
vegerot:pr1181

Conversation

@vegerot
Copy link

@vegerot vegerot commented Feb 18, 2026

Add Jump as a new source for the zoxide import command. The implementation includes a parser for Jump's data format and updates to all shell
completions (bash, fish, zsh, nu, fig) to include "jump" as an option.

@vegerot vegerot force-pushed the pr1181 branch 7 times, most recently from 3045a5c to ac452ca Compare February 18, 2026 01:24
@ajeetdsouza
Copy link
Owner

Use serde_json for this - after LTO, it doesn't increase the binary size by much.

Add [Jump](https://github.com/gsamokovarov/jump) as a new source for the `zoxide import` command. The implementation
includes a custom JSON parser for Jump's data format and updates to all shell
completions (bash, fish, zsh, nu, fig) to include "jump" as an option.
Copilot AI review requested due to automatic review settings March 10, 2026 04:01
@vegerot
Copy link
Author

vegerot commented Mar 10, 2026

@ajeetdsouza done! btw I can shave off another 60 lines to this patch if I replace my handrolled timestamp parser with chrono. The one I wrote works fine, but lmk

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Jump as a new import source for the zoxide import command, extending the existing importer set (autojump, z) and updating shell completions accordingly.

Changes:

  • Add ImportFrom::Jump CLI option and route it to a new import_jump implementation.
  • Implement Jump JSON deserialization and timestamp parsing for last_accessed.
  • Update completions (bash, fish, zsh, nu, fig) and add serde_json dependency.

Reviewed changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/cmd/import.rs Adds Jump importer, ISO8601 timestamp parsing helper, and tests.
src/cmd/cmd.rs Extends ImportFrom enum with Jump variant for Clap parsing.
contrib/completions/zoxide.ts Adds "jump" to Fig suggestions for zoxide import --from.
contrib/completions/zoxide.nu Adds "jump" to Nushell completion list for --from.
contrib/completions/zoxide.fish Adds jump to Fish completion entries for --from.
contrib/completions/zoxide.bash Adds jump to Bash completion word list for --from.
contrib/completions/_zoxide Adds jump to Zsh completion options for --from.
Cargo.toml Adds serde_json dependency.
Cargo.lock Locks serde_json into dependency graph.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +82 to +123
/// Parse a simple ISO 8601 UTC timestamp (YYYY-MM-DDTHH:MM:SSZ
/// or YYYY-MM-DDTHH:MM:SS.ssssss±hh:mm) to Unix epoch seconds.
/// Returns None if the format is invalid
/// Note: this only return to second-precision and ignores
/// timezone offsets
fn parse_iso8601_utc(timestamp: &str) -> Option<u64> {
// Expected format: 2023-01-01T12:00:00Z or 2024-11-07T11:01:57.327507-08:00
let is_valid = (timestamp.len() == 20 && timestamp.ends_with('Z'))
|| timestamp.len() >= 21;
if !is_valid {
return None;
}

let parts: Vec<&str> = timestamp[..19].split(&['-', 'T', ':'][..]).collect();
if parts.len() != 6 && parts.len() != 7 {
return None;
}

let year = parts[0].parse::<u64>().ok()?;
let month = parts[1].parse::<u32>().ok()?;
let day = parts[2].parse::<u32>().ok()?;
let hour = parts[3].parse::<u32>().ok()?;
let minute = parts[4].parse::<u32>().ok()?;
let second = parts[5].parse::<u32>().ok()?;

// Basic validation
if !(1..=12).contains(&month)
|| !(1..=31).contains(&day)
|| hour > 23
|| minute > 59
|| second > 59
{
return None;
}

// Simple calculation (ignoring leap years and timezone complexities for now)
// This is a basic implementation that works for most practical cases
let mut days_since_epoch = (year - 1970) * 365 + (year - 1970) / 4; // basic leap years
// rough month lengths (non-leap year)
let month_days = [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
for m in 1..month {
days_since_epoch += month_days[m as usize] as u64;
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_iso8601_utc claims to parse ISO 8601 to Unix epoch seconds, but the implementation explicitly ignores timezone offsets and does not handle leap years correctly (e.g., any 2024 date after Feb will be off by 86400s). Since last_accessed affects scoring/TTL, this will import incorrect timestamps from Jump. Consider parsing RFC3339 properly (including offsets) via a time/date library, or restrict accepted inputs to ...Z and document that limitation.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2024 is two years ago and I'm not worried about those scores being wrong

Comment on lines +118 to +120
// This is a basic implementation that works for most practical cases
let mut days_since_epoch = (year - 1970) * 365 + (year - 1970) / 4; // basic leap years
// rough month lengths (non-leap year)
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let mut days_since_epoch = (year - 1970) * 365 + ... will underflow if the input year is < 1970 (since year is u64), producing a huge timestamp instead of rejecting it. Add an explicit year >= 1970 check (or use a signed type) before doing the subtraction.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not worried about pre-1970 timestamps being buggy

Comment on lines +255 to +268
#[test]
fn parse_iso8601_timestamp() {
// Test basic ISO 8601 UTC timestamp parsing
// These are the actual values our parser produces (approximate calculation)
assert_eq!(parse_iso8601_utc("2023-01-01T12:00:00Z"), Some(1672574400)); // 12:00 UTC
assert_eq!(parse_iso8601_utc("2023-01-02T14:20:00Z"), Some(1672669200)); // 14:20 UTC
assert_eq!(parse_iso8601_utc("2023-01-03T09:15:00Z"), Some(1672737300)); // 09:15 UTC

// test stripping parts we ignore
assert_eq!(parse_iso8601_utc("2024-11-07T11:01:57.327507-08:00"), Some(1730890917));
assert_eq!(parse_iso8601_utc("2024-11-07T11:28:33.949702-08:00"), Some(1730892513));
assert_eq!(parse_iso8601_utc("2026-02-17T11:36:17.7596-08:00"), Some(1771328177));
// nanosecond precision (real Jump timestamps)
assert!(parse_iso8601_utc("2025-01-10T20:51:04.217017979-08:00").is_some());
Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The parse_iso8601_timestamp test hard-codes expected epoch values that match the current approximate (and incorrect for leap years/offsets) implementation. This will lock in wrong behavior rather than validating correct ISO 8601/RFC3339 parsing. Once timestamp parsing is fixed to handle leap years and timezone offsets, update these expected values to the real Unix timestamps (and add a case that exercises a leap day / post-Feb leap-year date).

Copilot uses AI. Check for mistakes.
Comment on lines +152 to +154
let entries: Vec<Entry> =
serde_json::from_str(buffer).context("invalid Jump JSON format")?;

Copy link

Copilot AI Mar 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description mentions a "custom JSON parser" for Jump, but the implementation uses serde_json::from_str. If the intent is to avoid bringing in serde_json, this doesn't meet that goal; otherwise, consider updating the PR description to reflect the actual approach (custom timestamp parsing + serde_json deserialization).

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants